package yunjiao.javatutorials.guava.concurrent;

import com.google.common.util.concurrent.Striped;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 缓存更新控制
 *
 * @author yangyunjiao
 */
public class CacheUpdateController {
    private final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();
    private final Striped<Semaphore> updateLocks = Striped.semaphore(20, 1);

    /**
     * 获取缓存数据 - 使用信号量防止缓存击穿
     */
    public String getFromCache(String key) {
        String value = cache.get(key);
        if (value != null) {
            return value; // 缓存命中
        }

        // 缓存未命中，获取更新锁
        Semaphore lock = updateLocks.get(key);
        try {
            if (lock.tryAcquire(500, TimeUnit.MILLISECONDS)) {
                try {
                    // 双重检查，防止重复更新
                    value = cache.get(key);
                    if (value == null) {
                        // 模拟从数据库加载数据
                        value = loadFromDatabase(key);
                        cache.put(key, value);
                        System.out.println("缓存更新完成: " + key);
                    }
                    return value;
                } finally {
                    lock.release();
                }
            } else {
                // 等待其他线程更新完成
                return waitForUpdate(key);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        }
    }

    private String loadFromDatabase(String key) throws InterruptedException {
        System.out.println("从数据库加载: " + key);
        Thread.sleep(2000); // 模拟数据库查询
        return "data_for_" + key;
    }

    private String waitForUpdate(String key) throws InterruptedException {
        int retry = 0;
        while (retry++ < 10) {
            String value = cache.get(key);
            if (value != null) {
                return value;
            }
            Thread.sleep(100);
        }
        throw new RuntimeException("获取缓存超时: " + key);
    }

    public static void main(String[] args) throws InterruptedException {
        CacheUpdateController controller = new CacheUpdateController();

        // 测试并发缓存访问
        Thread[] threads = new Thread[5];
        for (int i = 0; i < threads.length; i++) {
            final String key = "cache_key_" + (i % 2); // 只有2个不同的key
            threads[i] = new Thread(() -> {
                try {
                    String result = controller.getFromCache(key);
                    System.out.println(Thread.currentThread().getName() +
                            " 获取到: " + result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            threads[i].start();
        }

        for (Thread thread : threads) {
            thread.join();
        }
    }
}
